package risoe.syslab.fhnew.controller.plugin;

/*
 * Copyright (c) 2012-2014, Technical University of Denmark (DTU)
 * All rights reserved.
 * 
 * The Flexhouse 2.0 platform ("this software") is licensed under the
 * BSD 3-clause license.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *   - Neither the name of DTU nor the names of its contributors may be used to
 *     endorse or promote products derived from this software without specific
 *     prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE TECHNICAL UNIVERSITY OF DENMARK BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.HashMap;

import org.apache.batik.bridge.BridgeContext;
import org.apache.batik.bridge.DocumentLoader;
import org.apache.batik.bridge.UserAgentAdapter;
import org.apache.batik.bridge.GVTBuilder;
import org.apache.batik.dom.svg.SVGDOMImplementation;
import org.apache.batik.dom.svg.SVGOMTextElement;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.w3c.dom.svg.SVGDocument;
import org.w3c.dom.svg.SVGRect;

import risoe.syslab.fhnew.gui.VisualizationEvent.EventType;
import risoe.syslab.fhnew.common.SystemConstants.DeviceType;
import risoe.syslab.fhnew.config.ActuatorConfig;
import risoe.syslab.fhnew.controller.ControllerConfig;
import risoe.syslab.fhnew.controller.PluginController;
import risoe.syslab.fhnew.gui.DocumentUtils;
import risoe.syslab.fhnew.gui.VisualizationEvent;
import risoe.syslab.fhnew.gui.VisualizationUpdate;

import risoe.syslab.model.CompositeMeasurement;

public class DeviceTester extends PluginController {
	public static final String svgNS = "http://www.w3.org/2000/svg";
	public static final DOMImplementation domImpl = SVGDOMImplementation.getDOMImplementation();
	public static final String NAME = "DeviceTester";
	public static final String USER = "masv";

	private static SVGDocument mDocument;
	private static ArrayList<String> mSvgIdList = new ArrayList<String>();
	private static HashMap<String, Boolean> mStateMap = new HashMap<String, Boolean>();

	static {
		System.setProperty("java.awt.headless", "true");
	}

	public static void main(String[] args) {
		try {
			String configFile = args.length > 0? args[0]: null;
			new DeviceTester(NAME, USER, configFile);
		} catch (RemoteException e) {
			// RMI Connection was unsuccessful
			System.out.println("RMI Error: "+e.getMessage());
			System.exit(0);
		} catch (InstantiationException e) {
			// Controller name was already taken, or one of the requested devicetypes was already taken control of
			// another plugincontroller
			System.out.println("Argument Error: "+e.getMessage());
			System.exit(0);
		}
	}

	public DeviceTester(String name, String owner, String cfgfile)
			throws RemoteException, InstantiationException {
		super(name, owner, cfgfile);
	}

	@Override
	public void connectionEstablished() {
		// Set up visualization before registering the controller
		Document document = domImpl.createDocument(svgNS, "svg", null);

		// Create an instance of the SVG Generator
		Element svgRoot = document.getDocumentElement();
		svgRoot.setAttributeNS(null, "width", "1680");
		svgRoot.setAttributeNS(null, "height", "5080");

		Element rect = document.createElementNS(svgNS, "rect");
		rect.setAttributeNS(null, "x", "0");
		rect.setAttributeNS(null, "y", "0");
		rect.setAttributeNS(null, "width", "1680");
		rect.setAttributeNS(null, "height", "1080");
		rect.setAttributeNS(null, "style", "fill:#d7eef4");
		svgRoot.appendChild(rect);

		int indentX = 50, indentY = 50;
		int yOffset = 55;
		int maxY = 1080;

		int xPosition = indentX;
		int i = 0;
		String currentRoom = "";
		for (ActuatorConfig act : getBuildingConfig().getAllActuators()) {
			String room = getBuildingConfig().getRoomForActuator(act.getName()).getRoomName();
			if (!room.equals(currentRoom)) {
				int position = indentY + (i == 0? 0: (i+1)) * yOffset;
				if (position < maxY) {
					createText(document, room, xPosition, position, "30");
					i += (i == 0? 1: 2);
				} else {
					xPosition += 450;
					createText(document, room, xPosition, indentY, "30");
					i = 1;
				}

				// Create header text
				currentRoom = room;
			}

			int position = indentY + i * yOffset;
			if (position < maxY) {
				createText(document, act.getName(), xPosition, position, "20");
				i += 1;
			} else {
				xPosition += 450;
				createText(document, act.getName(), xPosition, indentY, "20");
				i = 1;
			}
			mSvgIdList.add(act.getName());
		}

		// Build the document. This allows the system to measure screen 
		// size etc, and results in a document where bounding boxes are available.
		UserAgentAdapter userAgent = new UserAgentAdapter();
		DocumentLoader loader = new DocumentLoader(userAgent);
		BridgeContext ctx = new BridgeContext(userAgent, loader);
		ctx.setDynamicState(BridgeContext.DYNAMIC);
		GVTBuilder builder = new GVTBuilder();
		builder.build(ctx, document);

		NodeList nodelist = document.getElementsByTagName("text");

		int widthPadding = 40;
		int heightPadding = 30;

		for (int j=0; j < nodelist.getLength(); j++) {
			SVGOMTextElement textElem = (SVGOMTextElement)nodelist.item(j);
			SVGRect bbox = textElem.getBBox();

			String actuatorName = textElem.getTextContent().replace("_text", "");

			// Add to state map
			mStateMap.put(actuatorName, false);

			Element rectangle = document.createElementNS(svgNS, "rect");
			if (bbox != null){
				rectangle.setAttributeNS(null, "x", String.valueOf(bbox.getX() - widthPadding/2));
				rectangle.setAttributeNS(null, "y", new Float(bbox.getY() - heightPadding/2).toString());
				rectangle.setAttributeNS(null, "width", String.valueOf(bbox.getWidth() + widthPadding));
				rectangle.setAttributeNS(null, "height", new Float(bbox.getHeight() + heightPadding).toString());
				rectangle.setAttributeNS(null, "fill-opacity", "0.3");
				rectangle.setAttributeNS(null, "id", actuatorName);
				rectangle.setAttributeNS(null, "stroke", "#ffffff");
				rectangle.setAttributeNS(null, "stroke-width", "1");
			}

			// Get current device state
			CompositeMeasurement value = getActuatorValue(actuatorName);
			if (value != null) {
				boolean state = value.value > 0.5? true: false;
				mStateMap.put(actuatorName, state);
				if (state) {
					rectangle.setAttributeNS(null, "fill", "#00ff00");
				} else {
					rectangle.setAttributeNS(null, "fill", "#ff0000");
				}
			} else {
				rectangle.setAttributeNS(null, "fill-opacity", "0.0");
				rectangle.setAttributeNS(null, "stroke-opacity", "0.0");
			}
			document.getDocumentElement().appendChild(rectangle);
		}

		mDocument = (SVGDocument) document;
	}

	private static void createText(Document document, String text, int x, int y, String size) {
		Element svgRoot = document.getDocumentElement();

		// Add text
		Element txtElem = document.createElementNS(svgNS, "text");
		txtElem.setAttribute("id", text + "_text");
		txtElem.setAttribute("x", String.valueOf(x));
		txtElem.setAttribute("y", String.valueOf(y));
		txtElem.setAttribute("style", "font-family:Arial;font-size:"+size+";fill:#000000;");
		txtElem.setTextContent(text);

		// Add to document
		svgRoot.appendChild(txtElem);
	}


	@Override
	public void onVisualizationEvent(VisualizationEvent event) {
		String id = event.getID();

		if (id != null && !id.isEmpty()) {
			if (event.getEventType() == EventType.Mouse_Click){
				System.out.println("Element clicked: "+id);

				Element rect = mDocument.getElementById(id);
				if (rect == null) {
					System.out.println("No rectangle found for element: " + id);
				} else {
					VisualizationUpdate update = new VisualizationUpdate(NAME);

					boolean state = mStateMap.get(id);
					if (state) {
						// Turn off device
						update.addCommand(id, "fill", "#ff0000");
						setActuator(id, new CompositeMeasurement(0.0));
					} else {
						// Turn on device
						update.addCommand(id, "fill", "#00ff00");
						setActuator(id, new CompositeMeasurement(1.0));
					}
					mStateMap.put(id, !state);

					executeVisualizationUpdate(update);
				}
			}
		}
	}

	@Override
	public ControllerConfig supplyControllerConfig(ControllerConfig config) {
		for (DeviceType type : DeviceType.values()) {
			if (type.mIsActuator) {
				config.addControlledDeviceType(type);
			}
		}
		for (String id : mSvgIdList) {
			config.addVisualizationListener(id);
		}
		config.setSerializedVisualization(DocumentUtils.serializeDocument(mDocument));
		return config;
	}
}
